/*
 * Copyright (C) 2010-2011 sunjumper@163.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package mfinder.spring;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import mfinder.ActionFactory;
import mfinder.ActionInvocation;
import mfinder.MFinderException;
import mfinder.ObjectFactory;
import mfinder.config.Configuration;
import mfinder.impl.DefaultActionFactory;
import mfinder.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;

/**
 * 提供与springframework集成的ActionFactory。Action指定path属性的注入由指定springframework的bean完成。
 */
public class DefaultActionFactoryBean implements FactoryBean<ActionFactory>, InitializingBean,
        DisposableBean, ApplicationContextAware {

    /** LOG */
    private static final Logger LOG = LoggerFactory.getLogger(DefaultActionFactoryBean.class);

    /**
     * Location of a single MFinder XML config file.
     * 不保证ActionFactory属性的重复加载。
     */
    private Resource configLocation;

    /* ActionFactory类 */
    private Class<? extends ActionFactory> actionFactoryClass = null;

    /* Action工厂 */
    private ActionFactory actionFactory;

    /** @see DefaultActionFactory#defaultInterceptorStack */
    private String defaultInterceptorStack = null;

    /** @see DefaultActionFactory#defaultResultType */
    private String defaultResultType = null;

    /** @see DefaultActionFactory#actionInvocationClass */
    private Class<? extends ActionInvocation> actionInvocationClass;

    /** @see DefaultActionFactory#extension */
    private String extension = null;

    /** @see DefaultActionFactory#actionCacheNumber */
    private int actionCacheNumber = -1;

    /** @see DefaultActionFactory#objectFactory */
    private ObjectFactory objectFactory;

    /* 拦截器的bean名称和类名称的集合 */
    private List<Object> interceptors = null;

    /* 拦截栈的bean名称和类名称的集合 */
    private List<Object> interceptorStacks = null;

    /* 结果类型的bean名称和类名称的集合 */
    private List<Object> resultTypes = null;

    /* 结果对象的bean名称和类名称的集合 */
    private List<Object> results = null;

    /* Action的bean名称和类名称的集合 */
    private List<Object> actions = null;

    /**
     * 未完成
     */
    private List<String> scanPackages;

    /**
     * 未完成
     */
    private List<String> excludePackages;

    /**
     * 未完成
     * 自动检测引入的包名。
     *
     * @see Configuration#scanComponents
     */
    protected Set<Class<?>> scanComponents = new LinkedHashSet<Class<?>>();

    /**
     * 未完成
     * 排除的包或类。
     *
     * @see Configuration#excludeComponents
     */
    protected Set<String> excludeComponents = new HashSet<String>();

    /**
     * springframework 的ApplicationContext对象。
     */
    private ApplicationContext applicationContext;

    /**
     * 返回ActionFactory。
     * 如果ActionFactory未实例化则抛出异常。
     *
     * @return 不为<code>null</code>)的ActionFactory
     *
     * @throws IllegalStateException 如果ActionFactory未实例化。
     */
    protected final ActionFactory getActionFactory() {
        if (this.actionFactory == null) {
            throw new IllegalStateException("ActionFactory not initialized yet");
        }
        return this.actionFactory;
    }

    /**
     * 初始化ActionFactory。
     *
     * @see #buildActionFactory()
     * @see #afterActionFactoryCreation(mfinder.ActionFactory)
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("Initiating MFinder ActionFactory at : " + new java.util.Date());

        //不保证ActionFactory属性的重复加载
        if (configLocation != null) {
            LOG.info("Load configuration : " + configLocation.getURL());
            this.actionFactory = new Configuration().load(configLocation.getInputStream()).getFactory();
        } else {
            this.actionFactory = actionFactoryClass == null
                    ? new DefaultActionFactory()
                    : actionFactoryClass.newInstance();
        }
        afterActionFactoryCreation(actionFactory);

        this.actionFactory = buildActionFactory();

    }

    /**
     * 在bean工厂关闭时移除ActionFactory中所有关联关系。
     *
     * @throws MFinderException 如果发生错误。
     */
    @Override
    public void destroy() throws MFinderException {
        LOG.info("Closing MFinder ActionFactory : " + actionFactory);
        try {
            beforeActionFactoryDestruction();
        } finally {
            this.actionFactory.clear();
        }
    }

    /**
     * 返回ActionFactory对象，默认为单例状态。
     */
    @Override
    public ActionFactory getObject() {
        return this.actionFactory;
    }

    @Override
    public Class<? extends ActionFactory> getObjectType() {
        return actionFactory == null ? null : actionFactory.getClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    /**
     * 初始化 MFinder ActionFactory。
     *
     * @return 已初始化的ActionFactory。
     *
     * @throws Exception 如果初始化失败。
     */
    protected ActionFactory buildActionFactory() throws Exception {
        LOG.info("Building MFinder ActionFactory : " + actionFactory);
        if (actionFactory instanceof DefaultActionFactory) {
            DefaultActionFactory defaultFactory = (DefaultActionFactory) this.actionFactory;

            //set object factory, default use spring object facrtory
            defaultFactory.setObjectFactory(objectFactory == null
                    ? new SpringObjectFactory(applicationContext)
                    : objectFactory);

            if (StringUtil.isNotNull(defaultInterceptorStack))
                defaultFactory.setDefaultInterceptorStack(defaultInterceptorStack);
            if (StringUtil.isNotNull(defaultResultType))
                defaultFactory.setDefaultResultType(defaultResultType);
            if (actionInvocationClass != null)
                defaultFactory.setActionInvocationClass(actionInvocationClass);

            defaultFactory.setExtension(extension);
            if (actionCacheNumber > 0)
                defaultFactory.setActionCacheNumber(actionCacheNumber);

            if (interceptors != null) {
                for (Object obj : interceptors) {
                    defaultFactory.addInterceptors(
                            obj instanceof String
                            ? Class.forName((String) obj)
                            : obj);
                }
            }
            if (interceptorStacks != null) {
                for (Object obj : interceptorStacks) {
                    defaultFactory.addInterceptorStacks(
                            obj instanceof String
                            ? Class.forName((String) obj)
                            : obj);
                }
            }
            if (resultTypes != null) {
                for (Object obj : resultTypes) {
                    defaultFactory.addResultTypes(
                            obj instanceof String
                            ? Class.forName((String) obj)
                            : obj);
                }
            }
            if (results != null) {
                for (Object obj : results) {
                    defaultFactory.addResults(
                            obj instanceof String
                            ? Class.forName((String) obj)
                            : obj);
                }
            }
            if (actions != null) {
                for (Object obj : actions) {
                    defaultFactory.addActions(
                            obj instanceof String
                            ? Class.forName((String) obj)
                            : obj);
                }
            }
        }
        return actionFactory;
    }

    /**
     * Hook that allows post-processing after the ActionFactory has been
     * successfully created. The ActionFactory is already available through
     * <code>getActionFactory()</code> at this point.
     * <p>This implementation is empty.
     *
     * @param actionFactory ActionFactory。
     *
     * @see #getActionFactory()
     */
    protected void afterActionFactoryCreation(ActionFactory actionFactory) {
    }

    /**
     * Hook that allows shutdown processing before the ActionFactory
     * will be closed. The ActionFactory is still available through
     * <code>getActionFactory()</code> at this point.
     * <p>This implementation is empty.
     *
     * @see #getActionFactory()
     */
    protected void beforeActionFactoryDestruction() {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * Set the location of a single MFinder XML config file.
     *
     * @param configLocation Location of a single MFinder XML config file.
     *
     * @see mfinder.config.Configuration#load(java.io.InputStream)
     */
    public void setConfigLocation(Resource configLocation) {
        this.configLocation = configLocation;
    }

    /**
     * 返回Action的bean名称和类名称的集合。
     *
     * @return Action的bean名称和类名称的集合。
     */
    public List<Object> getActions() {
        return actions;
    }

    /**
     * 设置Action的bean名称和类名称的集合。
     *
     * @param actions Action的bean名称和类名称的集合。
     */
    public void setActions(List<Object> actions) {
        this.actions = actions;
    }

    /**
     * 设置ActionFactory类名称。
     *
     * @param actionFactoryClass ActionFactory类名称。
     */
    public void setActionFactoryClass(String actionFactoryClass) throws ClassNotFoundException {
        if (StringUtil.isNotNull(actionFactoryClass))
            this.actionFactoryClass = (Class<? extends ActionFactory>) Class.forName(actionFactoryClass);
    }

    /**
     * @see ActionFactory#getObjectFactory()
     */
    public ObjectFactory getObjectFactory() {
        return objectFactory;
    }

    /**
     * @see DefaultActionFactory#setObjectFactory(mfinder.ObjectFactory)
     */
    public void setObjectFactory(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory;
    }

    /**
     * @see ActionFactory#getDefaultInterceptorStack()
     */
    public String getDefaultInterceptorStack() {
        return defaultInterceptorStack;
    }

    /**
     * @see DefaultActionFactory#setDefaultInterceptorStack(java.lang.String)
     */
    public void setDefaultInterceptorStack(String defaultInterceptorStack) {
        this.defaultInterceptorStack = defaultInterceptorStack;
    }

    /**
     * @see ActionFactory#getDefaultResultType()
     */
    public String getDefaultResultType() {
        return defaultResultType;
    }

    /**
     * @see DefaultActionFactory#setDefaultResultType(java.lang.String)
     */
    public void setDefaultResultType(String defaultResultType) {
        this.defaultResultType = defaultResultType;
    }

    /**
     * @see DefaultActionFactory#setActionInvocationClass(java.lang.Class)
     */
    public void setActionInvocationClass(String actionInvocationClass) throws ClassNotFoundException {
        if (StringUtil.isNotNull(actionInvocationClass))
            this.actionInvocationClass = (Class<? extends ActionInvocation>) Class.forName(actionInvocationClass);
    }

    /**
     * @see DefaultActionFactory#getExtension()
     */
    public String getExtension() {
        return extension;
    }

    /**
     * @see DefaultActionFactory#setExtension(java.lang.String)
     */
    public void setExtension(String extension) {
        this.extension = extension;
    }

    /**
     * @see DefaultActionFactory#getActionCacheNumber()
     */
    public int getActionCacheNumber() {
        return actionCacheNumber;
    }

    /**
     * @see DefaultActionFactory#setActionCacheNumber(int)
     */
    public void setActionCacheNumber(int actionCacheNumber) {
        this.actionCacheNumber = actionCacheNumber;
    }

    /**
     * 返回拦截栈的bean名称和类名称的集合。
     *
     * @return 拦截栈的bean名称和类名称的集合。
     */
    public List<Object> getInterceptorStacks() {
        return interceptorStacks;
    }

    /**
     * 设置拦截栈的bean名称和类名称的集合。
     *
     * @param interceptorStacks 拦截栈的bean名称和类名称的集合。
     */
    public void setInterceptorStacks(List<Object> interceptorStacks) {
        this.interceptorStacks = interceptorStacks;
    }

    /**
     * 返回拦截器的bean名称和类名称的集合。
     *
     * @return 拦截器的bean名称和类名称的集合。
     */
    public List<Object> getInterceptors() {
        return interceptors;
    }

    /**
     * 设置拦截器的bean名称和类名称的集合。
     *
     * @param interceptors 拦截器的bean名称和类名称的集合。
     */
    public void setInterceptors(List<Object> interceptors) {
        this.interceptors = interceptors;
    }

    /**
     * 返回结果类型的bean名称和类名称的集合。
     *
     * @return 结果类型的bean名称和类名称的集合。
     */
    public List<Object> getResultTypes() {
        return resultTypes;
    }

    /**
     * 设置结果类型的bean名称和类名称的集合。
     *
     * @param resultTypes 结果类型的bean名称和类名称的集合。
     */
    public void setResultTypes(List<Object> resultTypes) {
        this.resultTypes = resultTypes;
    }

    /**
     * 返回结果对象的bean名称和类名称的集合。
     *
     * @return 结果对象的bean名称和类名称的集合。
     */
    public List<Object> getResults() {
        return results;
    }

    /**
     * 设置结果对象的bean名称和类名称的集合。
     *
     * @param results 结果对象的bean名称和类名称的集合。
     */
    public void setResults(List<Object> results) {
        this.results = results;
    }
}
